import logging
import os
import sys
import threading
import time
from datetime import datetime

from PyQt5.QtBluetooth import QBluetoothDeviceInfo, QLowEnergyController, QBluetoothUuid, QLowEnergyService
from PyQt5.QtCore import pyqtSignal, QSize, Qt, QCoreApplication, QEventLoop, QTimer
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLabel, QPlainTextEdit, QListWidgetItem, QApplication, QListWidget, \
    QVBoxLayout, QPushButton, QFrame

from app_config import resource_icon_path
from common import get_widget, APP_WARNING_MSG
from widget.service_node_widget import ServiceNodeWidget
from widget.node_content_widget import NodeContentWidget


class DeviceInfoWidget(QWidget):
    """
    主界面用于显示搜索到的设备信息列表
    """
    servicesChanged = pyqtSignal(list)

    def __init__(self, info: QBluetoothDeviceInfo, item, *args, **kwargs):
        super(DeviceInfoWidget, self).__init__(*args, **kwargs)
        self.device_info: QBluetoothDeviceInfo = info
        self.item = item
        self._custom_ui_init()
        self._signal_init()
        self.main_widget = get_widget('main_widget')
        # 把一个个服务节点ui加进来，形成的服务列表widget
        self.service_node_list_widget: NodeContentWidget = None
        # 服务node ui放进来，用于后续处理
        self.service_node_list = []
        self.service_uuid_list = []

    def _custom_ui_init(self):
        self.parent_hl = QHBoxLayout()
        self.parent_hl.setContentsMargins(6, 0, 6, 0)
        self.setLayout(self.parent_hl)
        # 最左边图标
        self.device_type_icon_lb = QLabel()
        self.device_type_icon_lb.setScaledContents(True)
        self.device_type_icon_lb.setFixedSize(QSize(48, 48))
        self.parent_hl.addWidget(self.device_type_icon_lb)

        # 中间设备名字
        self.device_info_te = QPlainTextEdit()
        self.device_info_te.setReadOnly(True)
        self.device_info_te.setFrameShape(QFrame.Panel)
        self.device_info_te.setFrameShadow(QFrame.Plain)
        self.device_info_te.setLineWidth(0)
        # self.device_info_te.setStyleSheet("QPlainTextEdit::hover{background-color:#E5F3FF}")
        self.parent_hl.addWidget(self.device_info_te)
        self.setStyleSheet("QWidget::hover{background-color:#E5F3FF}")

        self.ctrl_vl = QVBoxLayout()
        self.parent_hl.addLayout(self.ctrl_vl)
        # 连接按键
        self.connect_pb = QPushButton("连接")
        self.ctrl_vl.addWidget(self.connect_pb)

        # 右边信号信息
        self.rssi_info_hl = QHBoxLayout()
        self.ctrl_vl.addLayout(self.rssi_info_hl)

        self.rssi_lb = QLabel("0dBm")
        self.rssi_info_hl.addWidget(self.rssi_lb)

        self.scan_time_info_lb = QLabel("ms")
        self.rssi_info_hl.addWidget(self.scan_time_info_lb)

        if self.device_info is not None:
            self.set_device_brief_info(self.device_info.name() + '\n' + self.device_info.address().toString())
            self.set_info()

    def _signal_init(self):
        self.connect_pb.clicked.connect(self.on_connect_button_clicked)

    def set_device_type_icon(self, icon: QPixmap):
        self.device_type_icon_lb.setPixmap(icon)

    def set_device_brief_info(self, text: str):
        self.device_info_te.setPlainText(text)

    def set_device_rssi(self, rssi: int):
        self.rssi_lb.setText(str(rssi) + 'dBm')

    def set_device_scan_time_info(self, time: str):
        self.scan_time_info_lb.setText(time)

    def update_device_info(self, device: QBluetoothDeviceInfo):
        self.device_info = device

    def set_info(self):
        icon = os.path.join(resource_icon_path, 'default.png')
        self.set_device_type_icon(QPixmap(icon))
        self.set_device_rssi(self.device_info.rssi())

    def on_connect_button_clicked(self):
        try:
            if self.connect_pb.text() == "连接":
                connect_thread = threading.Thread(target=self.connect_thread_entry)
                connect_thread.start()
            else:
                self.m_control.disconnectFromDevice()
                self.connect_pb.setText("连接")
        except Exception as e:
            print(e)

    def connect_thread_entry(self):
        try:
            self.main_widget.msg_print("connect to:" + self.device_info.name())
            print("createCentral:" + str(datetime.now()))
            self.m_control = QLowEnergyController.createCentral(self.device_info)
            self.m_control.serviceDiscovered.connect(self.on_service_discovered)
            self.m_control.discoveryFinished.connect(self.on_service_discovery_finished)
            self.m_control.error.connect(self.on_controller_error)
            self.m_control.connected.connect(self.on_device_connected)
            self.m_control.disconnected.connect(self.on_device_disconnected)
            print("connectToDevice:" + str(datetime.now()))
            if self.m_control.state() == QLowEnergyController.UnconnectedState:
                self.m_control.connectToDevice()
                print("success connect:" + str(datetime.now()))
            else:
                print("state can not connect")
        except Exception as e:
            print(e)

    def on_service_discovered(self, uuid=QBluetoothUuid()):
        try:
            QCoreApplication.processEvents(QEventLoop.AllEvents)
            service = self.m_control.createServiceObject(uuid)
            if self.service_node_list_widget is not None:
                self.add_service_to_list(service)
        except Exception as e:
            print(e)

    def on_service_discovery_finished(self):
        print("on_service_discovery_finished")
        discover_details_thread = threading.Thread(target=self.service_discover_details_entry)
        discover_details_thread.start()

    def on_controller_error(self, error=QLowEnergyController.error):
        print("Controller Error")
        self.connect_pb.setText("连接")
        self.remove_device()

    def on_device_connected(self):
        self.main_widget.msg_print("device connected", APP_WARNING_MSG)
        try:
            self.main_widget.msg_print("Create device tab:{}".format(self.device_info.name()))
            self.connect_pb.setText("断开")
            self.service_node_list_widget = NodeContentWidget(None)
            self.service_node_list_widget.setObjectName("ServiceSubwidget")
            self.service_node_list_widget.title_name = "Service"
            self.main_widget.device_tw.addTab(self.service_node_list_widget, self.device_info.name())
            self.main_widget.device_tw.setCurrentIndex(self.main_widget.device_tw.count() - 1)
            # 在tab中创建一个widget list,把设备的service加到list
            self.main_widget.msg_print("Create service list")
        except Exception as e:
            print(e)

        self.m_control.discoverServices()

    def on_device_disconnected(self):
        self.connect_pb.setText("连接")
        # self.remove_device()
        self.main_widget.msg_print("Remote device disconnected", APP_WARNING_MSG)

    def remove_device(self):
        self.main_widget.msg_print("remove tab, because of disconnect or error")
        index = self.main_widget.device_tw.count() - 1
        if index != 0:
            self.main_widget.device_tw.removeTab(index)
        self.service_node_list_widget = None

    def add_service_to_list(self, service):
        try:
            service_node = ServiceNodeWidget(self.m_control, service, self.service_node_list_widget, len(self.service_node_list))
            self.service_node_list.append(service_node)
            self.service_node_list_widget.insert_item_to_parent_area(service_node)
        except Exception as e:
            print(e)

    def service_discover_details_entry(self):
        for service in self.service_node_list:
            if service.service.state() == QLowEnergyService.DiscoveryRequired:
                service.start_discover_details()
                while True:
                    if service.service.state() == QLowEnergyService.ServiceDiscovered:
                        break
                    else:
                        time.sleep(1)
            else:
                print("{} state is :{}".format(service.service.serviceName(), service.service.state()))


def add_new_device_item(list_widget: QListWidget, device):
    """
    添加扫描到的BLE设备到list widget
    :param list_widget: 呈现decie信息的父设备
    :param device:
    :return:
    """
    try:
        main_widget = get_widget("main_widget")
        item = QListWidgetItem()
        item.setSizeHint(QSize(0, 64))  # width use item.sizeHint().width() is unavailable
        item.setData(Qt.UserRole, device)
        list_widget.addItem(item)
        widget = DeviceInfoWidget(device, item)
        list_widget.setItemWidget(item, widget)
    except Exception as e:
        logging.error(e)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = DeviceInfoWidget(None)
    window.show()
    logging.info("app start")
    sys.exit(app.exec_())
